#!/usr/bin/env python3
""" Command Line Arguments for tools """
import sys
import gettext

from typing import Any, List, Dict

from lib.cli.args import FaceSwapArgs
from lib.cli.actions import DirOrFileFullPaths, DirFullPaths, FileFullPaths, Radio, Slider


# LOCALES
_LANG = gettext.translation("tools.alignments.cli", localedir="locales", fallback=True)
_ = _LANG.gettext


_HELPTEXT = _("This command lets you perform various tasks pertaining to an alignments file.")


class AlignmentsArgs(FaceSwapArgs):
    """ Class to parse the command line arguments for Alignments tool """

    @staticmethod
    def get_info() -> str:
        """ Obtain command information.

        Returns
        -------
        str
            The help text for displaying in argparses help output
         """
        return _("Alignments tool\nThis tool allows you to perform numerous actions on or using "
                 "an alignments file against its corresponding faceset/frame source.")

    @staticmethod
    def get_argument_list() -> List[Dict[str, Any]]:
        """ Collect the argparse argument options.

        Returns
        -------
        dict
            The argparse command line options for processing by argparse
        """
        frames_dir = _(" Must Pass in a frames folder/source video file (-fr).")
        faces_dir = _(" Must Pass in a faces folder (-fc).")
        frames_or_faces_dir = _(" Must Pass in either a frames folder/source video file OR a"
                                "faces folder (-fr or -fc).")
        frames_and_faces_dir = _(" Must Pass in a frames folder/source video file AND a faces "
                                 "folder (-fr and -fc).")
        output_opts = _(" Use the output option (-o) to process results.")
        argument_list = []
        argument_list.append(dict(
            opts=("-j", "--job"),
            action=Radio,
            type=str,
            choices=("draw", "extract", "from-faces", "missing-alignments", "missing-frames",
                     "multi-faces", "no-faces", "remove-faces", "rename", "sort", "spatial"),
            group=_("processing"),
            required=True,
            help=_("R|Choose which action you want to perform. NB: All actions require an "
                   "alignments file (-a) to be passed in."
                   "\nL|'draw': Draw landmarks on frames in the selected folder/video. A "
                   "subfolder will be created within the frames folder to hold the output.{0}"
                   "\nL|'extract': Re-extract faces from the source frames/video based on "
                   "alignment data. This is a lot quicker than re-detecting faces. Can pass in "
                   "the '-een' (--extract-every-n) parameter to only extract every nth frame.{1}"
                   "\nL|'from-faces': Generate alignment file(s) from a folder of extracted "
                   "faces. if the folder of faces comes from multiple sources, then multiple "
                   "alignments files will be created. NB: for faces which have been extracted "
                   "folders of source images, rather than a video, a single alignments file will "
                   "be created as there is no way for the process to know how many folders of "
                   "images were originally used. You do not need to provide an alignments file "
                   "path to run this job. {3}"
                   "\nL|'missing-alignments': Identify frames that do not exist in the alignments "
                   "file.{2}{0}"
                   "\nL|'missing-frames': Identify frames in the alignments file that do not "
                   "appear within the frames folder/video.{2}{0}"
                   "\nL|'multi-faces': Identify where multiple faces exist within the alignments "
                   "file.{2}{4}"
                   "\nL|'no-faces': Identify frames that exist within the alignment file but no "
                   "faces were detected.{2}{0}"
                   "\nL|'remove-faces': Remove deleted faces from an alignments file. The "
                   "original alignments file will be backed up.{3}"
                   "\nL|'rename' - Rename faces to correspond with their parent frame and "
                   "position index in the alignments file (i.e. how they are named after running "
                   "extract).{3}"
                   "\nL|'sort': Re-index the alignments from left to right. For alignments with "
                   "multiple faces this will ensure that the left-most face is at index 0."
                   "\nL|'spatial': Perform spatial and temporal filtering to smooth alignments "
                   "(EXPERIMENTAL!)").format(frames_dir, frames_and_faces_dir, output_opts,
                                             faces_dir, frames_or_faces_dir)))
        argument_list.append(dict(
            opts=("-o", "--output"),
            action=Radio,
            type=str,
            choices=("console", "file", "move"),
            group=_("processing"),
            default="console",
            help=_("R|How to output discovered items ('faces' and 'frames' only):"
                   "\nL|'console': Print the list of frames to the screen. (DEFAULT)"
                   "\nL|'file': Output the list of frames to a text file (stored within the "
                   "source directory)."
                   "\nL|'move': Move the discovered items to a sub-folder within the source "
                   "directory.")))
        argument_list.append(dict(
            opts=("-a", "--alignments_file"),
            action=FileFullPaths,
            dest="alignments_file",
            type=str,
            group=_("data"),
            # hacky solution to not require alignments file if creating alignments from faces:
            required=not any(val in sys.argv for val in ["from-faces", "-fr", "-frames_folder"]),
            filetypes="alignments",
            help=_("Full path to the alignments file to be processed. If you have input a "
                   "'frames_dir' and don't provide this option, the process will try to find the "
                   "alignments file at the default location. All jobs require an alignments file "
                   "with the exception of 'from-faces' when the alignments file will be generated "
                   "in the specified faces folder.")))
        argument_list.append(dict(
            opts=("-fc", "-faces_folder"),
            action=DirFullPaths,
            dest="faces_dir",
            group=_("data"),
            help=_("Directory containing extracted faces.")))
        argument_list.append(dict(
            opts=("-fr", "-frames_folder"),
            action=DirOrFileFullPaths,
            dest="frames_dir",
            filetypes="video",
            group=_("data"),
            help=_("Directory containing source frames that faces were extracted from.")))
        argument_list.append(dict(
            opts=("-een", "--extract-every-n"),
            type=int,
            action=Slider,
            dest="extract_every_n",
            min_max=(1, 100),
            default=1,
            rounding=1,
            group=_("extract"),
            help=_("[Extract only] Extract every 'nth' frame. This option will skip frames when "
                   "extracting faces. For example a value of 1 will extract faces from every "
                   "frame, a value of 10 will extract faces from every 10th frame.")))
        argument_list.append(dict(
            opts=("-sz", "--size"),
            type=int,
            action=Slider,
            min_max=(256, 1024),
            rounding=64,
            default=512,
            group=_("extract"),
            help=_("[Extract only] The output size of extracted faces.")))
        argument_list.append(dict(
            opts=("-m", "--min-size"),
            type=int,
            action=Slider,
            min_max=(0, 200),
            rounding=1,
            default=0,
            dest="min_size",
            group=_("extract"),
            help=_("[Extract only] Only extract faces that have been resized by this percent or "
                   "more to meet the specified extract size (`-sz`, `--size`). Useful for "
                   "excluding low-res images from a training set. Set to 0 to extract all faces. "
                   "Eg: For an extract size of 512px, A setting of 50 will only include faces "
                   "that have been resized from 256px or above. Setting to 100 will only extract "
                   "faces that have been resized from 512px or above. A setting of 200 will only "
                   "extract faces that have been downscaled from 1024px or above.")))
        return argument_list
